use crate::e_paper::{SpiDriverPin, HEIGHT, WIDTH};
use std::thread::sleep;
use std::time::Duration;
pub struct SpiContextV3<T: SpiDriverPin> {
    pin: T,
}
impl<T: SpiDriverPin> SpiContextV3<T> {
    pub fn new(pin: T) -> Self {
        Self { pin }
    }
    pub fn config(&mut self) -> anyhow::Result<()> {
        println!("config");
        self.pin.set_cs(true)?;
        self.pin.set_sck(false)?;
        Ok(())
    }
    pub fn reset(&mut self) -> anyhow::Result<()> {
        println!("reset");
        self.pin.set_cs(true)?;

        self.pin.set_rst(true)?;
        sleep(Duration::from_millis(1000));
        self.pin.set_rst(false)?;
        sleep(Duration::from_millis(1000));
        self.pin.set_rst(true)?;
        sleep(Duration::from_millis(1000));
        Ok(())
    }
    pub fn write_byte(&mut self, data: u8) -> anyhow::Result<()> {
        // println!("write_byte");
        self.pin.set_cs(false)?;
        let mut d = data;
        for _i in 0..8 {
            if d & 0x80 == 0 {
                self.pin.set_sdo(false)?;
            } else {
                self.pin.set_sdo(true)?;
            }
            d <<= 1;
            self.pin.set_sck(true)?;
            self.pin.set_sck(false)?;
        }
        self.pin.set_cs(true)?;
        Ok(())
    }
    pub fn write_bytes(&mut self, data: &[u8]) -> anyhow::Result<()> {
        for v in data {
            self.write_byte(*v)?;
        }
        Ok(())
    }
    pub fn send_cmd(&mut self, data: u8) -> anyhow::Result<()> {
        self.send_data_internal(data, true)
    }
    pub fn send_data(&mut self, data: u8) -> anyhow::Result<()> {
        self.send_data_internal(data, false)
    }
    fn send_data_internal(&mut self, data: u8, is_cmd: bool) -> anyhow::Result<()> {
        if is_cmd {
            self.pin.set_dc(false)?;
        } else {
            self.pin.set_dc(true)?;
        }
        self.pin.set_cs(false)?;
        self.write_byte(data)?;
        self.pin.set_cs(true)?;
        Ok(())
    }
    pub fn init(&mut self, _mode: u8) -> anyhow::Result<()> {
        self.config()?;

        self.reset()?;
        sleep(Duration::from_millis(10));

        self.send_cmd(0x4)?;
        self.wait_ready(); //waiting for the electronic paper IC to release the idle signal

        self.send_cmd(0x0)?; //panel setting
        self.send_data(0x0f)?; //LUT from OTP，128x296
        self.send_data(0x89)?; //Temperature sensor, boost and other related timing settings

        self.send_cmd(0x50)?; //VCOM AND DATA INTERVAL SETTING
        self.send_data(0x77)?; //WBmode:VBDF 17|D7 VBDW 97 VBDB 57
                               //WBRmode:VBDF F7 VBDW 77 VBDB 37  VBDR B7;

        self.send_cmd(0x61)?; //resolution setting
        self.send_data(0x68)?;
        self.send_data(0x00)?;
        self.send_data(0xD4)?;

        Ok(())
    }

    pub fn wait_ready(&mut self) {
        while self.pin.busy() {
            sleep(Duration::from_millis(10));
        }
    }

    pub fn turnon_display(&mut self) -> anyhow::Result<()> {
        println!("DISPLAY REFRESH");
        self.send_cmd(0x12)?;
        sleep(Duration::from_millis(50));
        self.wait_ready();
        Ok(())
    }

    pub fn sleep(&mut self) -> anyhow::Result<()> {
        println!("sleep mode");
        self.send_cmd(0x50)?; //DEEP_SLEEP_MODE
        self.send_data(0xf7)?;

        self.send_cmd(0x02)?; //power off
        self.wait_ready(); //waiting for the electronic paper IC to release the idle signal

        self.send_cmd(0x07)?; //deep sleep
        self.send_data(0xA5)?;
        Ok(())
    }

    pub fn clear(&mut self) -> anyhow::Result<()> {
        println!("clear display");
        let width = if WIDTH % 8 == 0 {
            WIDTH / 8
        } else {
            WIDTH / 8 + 1
        };
        let height = HEIGHT;
        //send black data
        self.send_cmd(0x10)?;
        for _j in 0..height {
            for _i in 0..width {
                self.send_data(0xff)?;
            }
        }
        //send red data
        self.send_cmd(0x13)?;
        for _j in 0..height {
            for _i in 0..width {
                self.send_data(0xff)?;
            }
        }
        self.turnon_display()?;
        Ok(())
    }
    pub fn display(&mut self, data: &[u8], red: &[u8]) -> anyhow::Result<()> {
        println!("show image len {}", data.len());
        let width = if WIDTH % 8 == 0 {
            WIDTH / 8
        } else {
            WIDTH / 8 + 1
        };
        let height = HEIGHT;
        if data.len() > 0 {
            self.send_cmd(0x10)?;
            for j in 0..height {
                for i in 0..width {
                    let index = (i + j * width) as usize;
                    if index >= data.len() {
                        continue;
                    }
                    self.send_data(data[index])?;
                }
            }
        }
        if red.len() > 0 {
            self.send_cmd(0x13)?;
            for j in 0..height {
                for i in 0..width {
                    let index = (i + j * width) as usize;
                    if index >= red.len() {
                        continue;
                    }
                    self.send_data(red[index])?;
                }
            }
        }
        if data.len() > 0 || red.len() > 0 {
            self.turnon_display()?;
        }
        Ok(())
    }

    pub fn stop_data(&mut self) -> anyhow::Result<()> {
        self.send_cmd(0x11)?;
        self.send_data(0x0)?;
        Ok(())
    }
    pub fn test(&mut self) -> anyhow::Result<()> {
        Ok(())
    }
}
